import { RequestOptions, RequestConfig, ResponseResult } from './types'
import { isAppPlus, isH5, isMp } from '@/utils/platform'
const config = Symbol('config')
const isCompleteURL = Symbol('isCompleteURL')
const requestBefore = Symbol('requestBefore')
const requestAfter = Symbol('requestAfter')
import { minCache } from '@/utils/min-cache'
import { router } from '@/router'
import qs from 'qs'
import store from '@/store/index'

const disableShowToastCode: any[] = []

export interface IRes {
  code?: string // 返回码 0表示正常，非0异常
  message?: string // 业务异常信息
  data?: any // 页数数据
  success?: boolean // 接口返回是否正常
  total?: number // 总数
  cookies?: any //
}

class MinRequest {
  [config]: RequestConfig = {
    baseURL: '',
    header: {
      'Content-Type': 'application/json',
    },
    method: 'GET',
    dataType: 'json',
    responseType: 'text',
  }

  interceptors = {
    request: (func: (...arg: any) => RequestConfig) => {
      if (func) {
        MinRequest[requestBefore] = func
      } else {
        MinRequest[requestBefore] = (request) => request
      }
    },
    response: (func: (...arg: any) => any) => {
      if (func) {
        MinRequest[requestAfter] = func
      } else {
        MinRequest[requestAfter] = (response) => response
      }
    },
  }

  static [requestBefore](config: RequestConfig) {
    return config
  }

  static [requestAfter](response: ResponseResult) {
    return response
  }

  static [isCompleteURL](url: string) {
    return /(http|https):\/\/([\w.]+\/?)\S*/.test(url)
  }

  setConfig(func: (config: RequestConfig) => any) {
    this[config] = func(this[config])
  }

  request(options: RequestOptions): Promise<any> {
    options.baseURL = options.baseURL || this[config].baseURL
    const fetch = minCache.get('fetch')
    if ((isAppPlus || isMp) && fetch) {
      options.baseURL = `http://${fetch.IP}:${fetch.port}`
    }

    options.dataType = options.dataType || this[config].dataType

    const accessDomain = minCache.get('accessDomain') || ''
    if (!isH5 && accessDomain) {
      options.baseURL = MinRequest[isCompleteURL](accessDomain) ? accessDomain : 'http://' + accessDomain
    }

    // #ifdef MP-WEIXIN
    if (options.data) {
      const newData = {}
      Object.keys(options.data).forEach((key) => {
        // @ts-ignore
        if (options.data[key] != undefined) {
          // @ts-ignore
          newData[key] = options.data[key]
        }
      })
      options.data = newData
    }
    // #endif
    options.header = {
      ...options.header,
      ...this[config].header,
    }

    options.method = options.method?.toUpperCase() || this[config].method.toUpperCase()

    options = {
      ...options,
      ...MinRequest[requestBefore](options),
    }
    options.url = MinRequest[isCompleteURL](options.url) ? options.url : options.baseURL + options.url

    // 兼容qs请求
    if (options.method.toUpperCase() === 'GET') {
      if (options.data && Object.keys(options.data).length) {
        const newData = qs.stringify(options.data, { arrayFormat: 'repeat' })

        delete options.data
        options.url = `${options.url}?${newData}`
      }
    }

    // console.log('url', options.url)

    return new Promise((resolve, reject) => {
      options.success = function (res: IRes) {
        // console.log(res, 'ok')
        let resData
        if (options.beforeResponse) {
          resData = options.beforeResponse(res)
        } else {
          resData = res.data
        }

        const resAfter: any = MinRequest[requestAfter](resData)

        const handleReLogin = async (resAfter) => {
          // 防止多次跳转登录页面
          await store.dispatch('login/logout')
          setTimeout(() => {
            router.replaceAll({
              name: 'UserLogin',
            })
          }, 1000)
          reject(resAfter)
        }
        const handleError = () => {
          const errMsg = resAfter.msg || resAfter.message

          if (errMsg && !disableShowToastCode.includes(resAfter.code) && !options.hideErrorMessage) {
            uni.showToast({
              title: errMsg,
              icon: 'none',
              mask: true,
              duration: 4500,
            })
          }
          reject(resAfter)
        }

        if (resAfter.code === 401) {
          handleReLogin(resAfter)
        } else if (resAfter.code === 500) {
          handleError()
        } else {
          // 200 或者没有状态码都用code判断
          if (+resAfter.code === 200 || resAfter.code === undefined) {
            resolve(resAfter)
          } else {
            handleError()
          }
        }
        uni.hideLoading()
      }
      options.fail = function (err) {
        // console.log(err, 'err')
        reject(MinRequest[requestAfter](err))
        uni.hideLoading()
      }

      if (isAppPlus && options.method == 'PATCH') {
        // @ts-ignore
        const xhr = new plus.net.XMLHttpRequest()
        xhr.onload = function (e: any) {
          console.log('onreadystatechange: ' + JSON.stringify(e))
        }
        xhr.onerror = function () {
          console.log('onreadystatechange: ' + xhr.readyState)
        }
        // xhr.open('PATCH', 'http://demo.dcloud.net.cn/test/xhr/post.php')
        const data = { name: 'HBuilder', version: '0.1.0' }
        xhr.setRequestHeader('Content-Type', 'application/json')
        // 发送HTTP请求
        xhr.send(JSON.stringify(data))
      } else {
        uni.request(options)
      }
    })
  }

  get(url: string, data = {}, options = {} as Partial<RequestOptions>) {
    options.url = url
    options.data = data
    options.method = 'GET'
    options.sslVerify = false
    return this.request(options as RequestOptions)
  }

  post(url: string, data = {}, options = {} as Partial<RequestOptions>) {
    options.url = url
    options.data = data
    options.method = 'POST'
    options.sslVerify = false
    return this.request(options as RequestOptions)
  }

  patch(url: string, data = {}, options = {} as Partial<RequestOptions>) {
    options.url = url
    options.data = data
    options.method = 'PATCH'
    return this.request(options as RequestOptions)
  }

  put(url: string, data = {}, options = {} as Partial<RequestOptions>) {
    options.url = url
    options.data = data
    options.method = 'PUT'
    return this.request(options as RequestOptions)
  }

  delete(url: string, data = {}, options = {} as Partial<RequestOptions>) {
    options.url = url
    options.data = data
    options.method = 'DELETE'
    return this.request(options as RequestOptions)
  }
}

export default MinRequest
